home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 June: Reference Library / Dev.CD Jun 96 RL / Dev.CD Jun 96 RL.toast / Technical Documentation / develop / develop Issue 24 / develop Issue 24 code / Scriptable Database 1.0a15 / Database / DatabaseDocument.h < prev    next >
Encoding:
Text File  |  1996-04-25  |  14.0 KB  |  381 lines  |  [TEXT/CWIE]

  1. //================================================================================
  2. // Greg Anderson
  3. // db+
  4. //
  5. // Abstract base class for DataBase document
  6. // 16,17 May 1994
  7. //================================================================================
  8. #pragma once
  9.  
  10. #ifndef __DATABASEDOCUMENT__
  11. #define __DATABASEDOCUMENT__
  12.  
  13. #include "ReferenceTemplates.h"
  14.  
  15. #include "Int64.h"
  16.  
  17. //
  18. // Needed for 'friend' delcaration
  19. //
  20. #include "AbstractRecord.h"
  21.  
  22. //
  23. // For 'Require'
  24. //
  25. #include "Exceptions.h"
  26.  
  27. class TAbstractBackingStore;
  28.  
  29. class TTransaction;
  30. class TGroupControlObject;
  31. class TAbstractRecord;
  32. class TDBProperty;
  33. class TDBElement;
  34.  
  35. enum
  36. {
  37.     kFormatIdentifier                    = 'db++',                            // Magic constant
  38.                 
  39.     //
  40.     // History of file format revision changes (hopefully not many
  41.     // of these will ever exist).
  42.     //
  43.     // I sort of wish I would have used zero to represent an invalid file
  44.     // format, and started the first revision with 1.  Oh well.
  45.     //
  46.     kInitialFileFormatRevision            = 0,                                // Initial release
  47.     
  48.     kFileFormatRevisionNumber            = kInitialFileFormatRevision,        // This is the revision that we will write
  49.     kEarliestCompatibleRevisionNumber    = kFileFormatRevisionNumber,        // This is the earliest revision that is forwards-compatable with what we wrote
  50.     kEarliestWriteCompatibleRevision    = kFileFormatRevisionNumber,        // This is the earliest revision that we will allow to write over our changes
  51.     kEarliestInterpretedRevisionNumber    = kInitialFileFormatRevision,        // This is the earliest revision that we can interpret and convert to the current format
  52.  
  53.     kNumberFreeLists                    = 64,                                // 64*4 = 256 bytes of free lists
  54.     kStartOfFreeList                    = 256,
  55.     kSizeOfFreeList                        = 256
  56. };
  57.  
  58. //
  59. // Critical persistant information about the document
  60. // is stored in the document file information header
  61. //
  62. class TDocumentFileInformation
  63. {
  64. public:
  65.     TDocumentFileInformation() :
  66.         fFormatIdentifier(kFormatIdentifier),
  67.         fEarliestCompatibleRevisionNumber(kEarliestCompatibleRevisionNumber),
  68.         fFileFormatRevisionNumber(kFileFormatRevisionNumber),
  69.         fEarliestWriteCompatibleRevision(kEarliestWriteCompatibleRevision),
  70.         fNumberOfHeaderBytes(sizeof(TDocumentFileInformation)),
  71.         fStartOfFreeList(kStartOfFreeList),
  72.         fStartOfRecordData(kStartOfFreeList + kSizeOfFreeList),
  73.         fNumberOfGroupsOnDisk(0),
  74.         fMetaRootID(-1)
  75.         {
  76.             this->AssignInitialID();
  77.         }
  78.     
  79.     void AssignInitialID();
  80.     
  81.     Int64            fDocumentID;                            // A unique ID identifying this document
  82.  
  83.     long            fFormatIdentifier;                        // A constant used to verify that this document is indeed in the correct format
  84.     long            fFileFormatRevisionNumber;                // The revision number of the file format used to write this document
  85.     long            fEarliestCompatibleRevisionNumber;        // The smallest document revision number that is forward-compatibile with the current one (i.e., can an old app ignore the fact that the doc format changed?)
  86.     long            fEarliestWriteCompatibleRevision;        // This will probably always == fFileFormatRevisionNumber
  87.  
  88.     long            fNumberOfHeaderBytes;                    // Sizeof(TDocumentFileInformation) for this file format revision
  89.     long            fStartOfFreeList;                        // Beginning of free list (offset from start of file)
  90.     long            fStartOfRecordData;                        // Beginning of database record data (offset from start of file)
  91.  
  92.     long            fNumberOfGroupsOnDisk;                    // How many record groups have been created and written to disk
  93.     long            fMetaRootID;                            // The Meta-root is almost always (always?) at node ID zero, but this record identifies it
  94.  
  95. };
  96.  
  97. //================================================================================
  98. // Class TDatabaseDocument
  99. //================================================================================
  100. class TDatabaseDocument
  101. {
  102.     //
  103.     // Group control objects are friends of DBDocuments
  104.     // because they need to be able to change the free list
  105.     // pointers when a record is pushed onto a free list
  106.     //
  107.     friend class TGroupControlObject;                            // MakeRecord, ReadRecordRange, WriteRecordRange, RemoveFromFreeList
  108.     
  109.     //
  110.     // Abstract records are allowed specific access to
  111.     // a couple of private routines
  112.     //
  113.     friend void TAbstractRecord::CacheGroupControlObject();                                // GetGroupControlObject
  114.     friend void TAbstractRecord::PushRecordIfFree(TTransaction* transaction);            // PushFreeRecordOntoFreeList
  115.     
  116.     //
  117.     // ----- Fields --------------------------------------------------------------
  118.     //
  119.     
  120. private:
  121.     //
  122.     // Transient fields (not written to disk)
  123.     //
  124.     TGroupControlObject**                fRecordGroupList;        // A pointer to an array of pointers to group control objects
  125.     long                                fNumberOfGroups;        // Number of record groups that may be loaded into memory
  126.     TAbstractBackingStore*                fBackingStore;            // Object in charge of reading/writing
  127.     
  128.     //
  129.     // Persistent fields (written to disk)
  130.     //
  131.     TDocumentFileInformation            fDocumentInformation;
  132.     long                                fFreeList[kNumberFreeLists];
  133.     
  134.     //
  135.     // ----- Methods -------------------------------------------------------------
  136.     //
  137.  
  138. public:
  139.                                         TDatabaseDocument() : fRecordGroupList(nil), fNumberOfGroups(0), fBackingStore(nil), fDocumentInformation()
  140.                                         {
  141.                                             //
  142.                                             // Clear out the free list
  143.                                             //
  144.                                             for(short i=0; i<kNumberFreeLists; ++i)
  145.                                                 fFreeList[i] = kNilIndex;
  146.                                             
  147.                                             //
  148.                                             // Create the meta-root, but don't use
  149.                                             // a transaction; this is just an initialization
  150.                                             // step, and if it fails the entire document
  151.                                             // is unusable.
  152.                                             //
  153.                                             this->CreateMetaRoot();
  154.                                         };
  155.                                         TDatabaseDocument(TAbstractBackingStore* backingStore) : fRecordGroupList(nil), fNumberOfGroups(0), fBackingStore(backingStore), fDocumentInformation()
  156.                                         {
  157.                                             this->ReadDocumentInformation();
  158.                                         };
  159.                                         
  160.     virtual                                ~TDatabaseDocument();
  161.  
  162.     //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  163.     // Methods of TTransactionAwareObject:
  164.     //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  165.     
  166.     //
  167.     // A documents key space is its document identifier; its key is
  168.     // unused, since the key space identifies the document.  We need
  169.     // to make sure that the key never interferes with keys used by
  170.     // records of the document, so we assign -1 (kNilIndex) to be
  171.     // the key for the document itself.
  172.     //
  173.     virtual Int64                        ObjectsKeySpace() const;
  174.     virtual long                        ObjectKey() const { return -1; };
  175.  
  176.     //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  177.     // Public interface:
  178.     //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  179.     
  180. public:
  181.     //
  182.     // Get the record that contains all of the trees in the
  183.     // database (the meta-root).
  184.     //
  185.     // A document must always have a Meta-Root.
  186.     //
  187.     // The properties of the document are stored as properties
  188.     // of the meta-root.  A document must always have a meta-root.
  189.     // It may further contain 0-N trees; the tree roots are stored
  190.     // as elements of the metaroot.
  191.     //
  192.     AConst<TDBElement>                    GetMetaRoot() { AConst<TAbstractRecord> metaRoot = this->GetRecordCursor(this->MetaRootIndex()); Require(metaRoot.Exists()); return metaRoot->DBElementCursor(); }
  193.  
  194.     //
  195.     // The methods for creating new records are not protected, but they
  196.     // still require a transaction in order to be called.  The newly-created
  197.     // cursor is always added to the transaction provided, and will be
  198.     // returned to the free list if the transaction's changes are discarded.
  199.     //
  200.     // n.b.        New properites and elements are not properties or elements of
  201.     //             any particular record until they are explicitly added to some
  202.     //            record in the database.
  203.     //
  204.     AnUpdate<TDBProperty>                NewDBProperty(TTransaction* transaction);
  205.     AnUpdate<TDBElement>                NewDBElement(TTransaction* transaction);
  206.     
  207.     //
  208.     // Perhaps this will be protected later; only DBProperties should ever
  209.     // create a data record.
  210.     //
  211.     AnUpdate<TDataRecord>                NewDataRecord(TTransaction* transaction, long sizeOfData);
  212.     
  213.     //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  214.     // Less common methods:
  215.     //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  216.     
  217.     //
  218.     // Most clients of the database document won't need to call
  219.     // "GetRecordCursor" directly, but it is legal to cache a
  220.     // record index and later recover a cursor to that element,
  221.     // if desired.
  222.     //
  223.     // Usually, though, all navagation will start from the meta-root.
  224.     //
  225.     AConst<TAbstractRecord>                GetRecordCursor(long recordIndex) const;
  226.     
  227.     //
  228.     // Without a backing-store object, the database object must remain
  229.     // RAM-resident for its entire existance; it cannot be saved, reloaded
  230.     // or partially paged out.  Setting the backing store object
  231.     // will also save the database to disk immediately, OVERWRITING the
  232.     // previous contents of the backing store with the current contents
  233.     // of the database.  To reload an existing database, the backing
  234.     // store must be provided to the database's constructor.
  235.     //
  236.     // This API allows an application to make a RAM-based database and
  237.     // associate it with a file later, if and only if it needs to save
  238.     // the database's contents.  Of course, portions of the database
  239.     // cannot be paged out unless it has a file to write into.
  240.     //
  241.     // Once the backing store is set, the database document owns it, and
  242.     // will delete it in its destructor.
  243.     //
  244.     void                                SetBackingStore(TAbstractBackingStore* backingStore);
  245.     
  246.     //
  247.     // Usually, a client should not need to look at the backing store
  248.     // object of a document; if you do call this method, though, remember
  249.     // that the document owns the backing store, and will delete it
  250.     // in its destructor.
  251.     //
  252.     TAbstractBackingStore*                GetBackingStore() const { return fBackingStore; }
  253.     
  254.     //
  255.     // Save a copy of this database in some other file.
  256.     //
  257.     void                                SaveACopy(TAbstractBackingStore* backingStore);
  258.  
  259.     //
  260.     // Normally, the database is lazy about writing changes back to
  261.     // disk.  If you wish, all changes can be flushed back to disk
  262.     // on demand.
  263.     //
  264.     void                                FlushChangesToDisk();
  265.     
  266.     //
  267.     // This method returns true if the document has had changes made
  268.     // to it that have not yet been written out to disk.  Will return
  269.     // true even if the document has no backing store object.
  270.     //
  271.     Boolean                                DocumentNeedsSave();
  272.     
  273.     //
  274.     // Someone might care if the document can be saved to disk or not.
  275.     // This method returns true if and only if this document has a backing
  276.     // store object that has a valid, open file
  277.     //
  278.     Boolean                                CanSaveDocument();
  279.  
  280.     //
  281.     // This method asks the backing store what the name of this
  282.     // document is
  283.     //
  284.     void                                DocumentName(TUpdataDataReference& name);
  285.     
  286.     //
  287.     // Some clients need to identify the metaroot. Perhaps exporting
  288.     // this index is a very bad way to identify the metaroot.  At the
  289.     // very least, this should probably be a protected method, as it
  290.     // doesn't really need to be part of TDatabaseDocument's exported API.
  291.     //
  292.     long                                MetaRootIndex() { return fDocumentInformation.fMetaRootID; }
  293.     
  294.     //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  295.     // Protected methods:
  296.     //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  297.     
  298. protected:
  299.     
  300.     TGroupControlObject*                GetGroupControlObject(long recordIndex) const;
  301.  
  302.     TAbstractRecord*                    GetRecord(long recordIndex) const;
  303.     TAbstractRecord*                    MakeRecord(long recordIndex, long recordIDWord);
  304.     
  305.     virtual void                        ReadRecordRange(void* bufferStart, long byteOffsetToRecord, long numberOfBytes, TAbstractBackingStore* backingStoreToUse = nil);
  306.     virtual void                        WriteRecordRange(void* bufferStart, long byteOffsetToRecord, long numberOfBytes, TAbstractBackingStore* backingStoreToUse = nil);
  307.  
  308.     virtual long                        GetFirstFreeIndex(long whichFreeList) const;
  309.     virtual void                        SetFreeIndex(long whichFreeList, long firstFree);
  310.  
  311.     long                                GetNextFreeIndex(long afterWhichFreeIndex) const;
  312.     long                                GetPreviousFreeIndex(long beforeWhichIndex) const;
  313.     long                                PopIndexFromFreeList(long whichFreeList);
  314.     void                                PushFreeRecordOntoFreeList(long recordToFree);
  315.     void                                RemoveFromFreeList(long recordToRemove);
  316.     
  317.     void                                VerifyFreeLists() const;
  318.     
  319.     //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  320.     // Private methods:
  321.     //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  322.     
  323. private:
  324.     long                                InitializeNewGroup(TGroupControlObject* group);
  325.     long                                InitializeNewDataGroup(TGroupControlObject* group, long desiredEncodedPhysicalSize);
  326.     void                                CreateGroupList(long numberOfGroups);
  327.     void                                CacheGroupControlObject(TGroupControlObject* newGroup);
  328.     TGroupControlObject*                CreateGroupControlObject(long firstRecord);
  329.     long                                MakeMoreFreeRecords(long addToWhichFreeList);
  330.     
  331.     void                                WriteDocumentInformation(TAbstractBackingStore* backingStoreToUse = nil);
  332.     void                                ReadDocumentInformation();
  333.     
  334.     void                                CreateMetaRoot();
  335. };
  336.  
  337. //================================================================================
  338. // Class TAbstractBackingStore
  339. //
  340. // A backing-store object is used to save, reload and page out a database
  341. //================================================================================
  342. class TAbstractBackingStore
  343. {
  344. protected:
  345.     Boolean                                fCanWrite;
  346.     
  347. public:
  348.     TAbstractBackingStore() : fCanWrite(true) {}
  349.     virtual ~TAbstractBackingStore();
  350.  
  351.     //
  352.     // For downcast-tests
  353.     //
  354.     virtual long                        BackingStoreType() const = 0;
  355.  
  356.     //
  357.     // CanSaveDocument should return 'true' if this backing store object
  358.     // has a valid, open file (or equivalent) to save the database into
  359.     // AND the fCanWrite boolean has not been clear
  360.     //
  361.     virtual Boolean                        CanSaveDocument();
  362.     void                                SetWriteEnable(Boolean canWrite) { fCanWrite = canWrite; }
  363.     
  364.     //
  365.     // Read/Write are passed:
  366.     //
  367.     //        bufferStart - a memory buffer to write from / read into
  368.     //        startByteInFile - the offset from the beginning of the database to begin reading from
  369.     //        numberOfBytes - the number of bytes to read/write
  370.     //
  371.     virtual void                        Read(void* bufferStart, Int64 startByteInFile, long numberOfBytes) = 0;
  372.     virtual void                        Write(void* bufferStart, Int64 startByteInFile, long numberOfBytes) = 0;
  373.  
  374.     //
  375.     // Return the name of the file associated with this backing store
  376.     //
  377.     virtual void                        DocumentName(TUpdataDataReference& name) = 0;
  378. };
  379.  
  380. #endif
  381.